/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * Data Block Collection Class
 *      Provides a custom collection class for storing DataBlock instances.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;
using Adaptive.Foundation.Collections;

namespace Adaptive.Foundation.IO
{
    /// <summary>
    /// Provides a custom collection class for storing DataBlock instances.
    /// </summary>
    public class DataBlockCollection : GenericCollectionBase<DataBlock>
    {
        /*--------------------------------------------------------------------------------
         * Private Constants Declarations
         *------------------------------------------------------------------------------*/
        #region Private Constants Declarations
        /// <summary>
        /// Default size of the data block.
        /// </summary>
        private const int DEF_BLOCK_SIZE = 65536;
        /// <summary>
        /// Error message.
        /// </summary>
        private const string BAD_SIZE_MSG = "Data block sizes must be in 4K sizes (multiples of 4096).";
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// Size of the data blocks.
        /// </summary>
        private int _blockSize = DEF_BLOCK_SIZE;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public DataBlockCollection()
            : base()
        {
        }
        /// <summary>
        /// Initializes a new instance of the class to the data in the 
        /// provided <see cref="Stream"/>.
        /// </summary>
        /// <param name="stream">
        /// An open <see cref="Stream"/> instance.
        /// </param>
        public DataBlockCollection(Stream stream) : base()
        {
            ReadDataBlocks(stream);
        }
        /// <summary>
        /// Initializes a new instance of the class to the data in the 
        /// supplied reader instance.
        /// </summary>
        /// <param name="reader">
        /// A <see cref="BinaryReader"/> instance used to read data from an
        /// open stream.
        /// </param>
        public DataBlockCollection(BinaryReader reader) : base()
        {
            ReadDataBlocks(reader);
        }
        /// <summary>
        /// Initializes a new instance of the class to the data in the 
        /// provided <see cref="Stream"/>.
        /// </summary>
        /// <param name="stream">
        /// An open <see cref="Stream"/> instance.
        /// </param>
        /// <param name="blockSize">
        /// The size of the data contained in the data block.
        /// </param>
        public DataBlockCollection(Stream stream, int blockSize) : base()
        {
            if (blockSize % 4096 != 0)
                throw new ArgumentOutOfRangeException(BAD_SIZE_MSG);

            _blockSize = blockSize;
            ReadDataBlocks(stream);
        }
        /// <summary>
        /// Initializes a new instance of the class to the data in the 
        /// supplied reader instance.
        /// </summary>
        /// <param name="reader">
        /// A <see cref="BinaryReader"/> instance used to read data from an
        /// open stream.
        /// </param>
        /// <param name="blockSize">
        /// The size of the data contained in the data block.
        /// </param>
        public DataBlockCollection(BinaryReader reader, int blockSize) : base()
        {
            if (blockSize % 4096 != 0)
                throw new ArgumentOutOfRangeException(BAD_SIZE_MSG);

            _blockSize = blockSize;
            ReadDataBlocks(reader);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods / Event Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods / Event Methods
        /// <summary>
        /// Gets the unique reference key value used to reference the instance once stored
        /// in the collection instance.
        /// </summary>
        /// <param name="itemToGetKeyFor">
        /// The object being appended to the collection instance.
        /// </param>
        /// <returns>
        /// A reference key value.
        /// </returns>
        protected override string GetKeyForObject(DataBlock itemToGetKeyFor)
        {
            if (itemToGetKeyFor == null)
                return string.Empty;
            else
                return itemToGetKeyFor.Sequence.ToString();
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Copies the contents of the collection to a stream.
        /// </summary>
        /// <returns>
        /// A new <see cref="MemoryStream"/> instance used to store the data content.
        /// </returns>
        public MemoryStream ToStream()
        {
            MemoryStream returnValue = null;
            BinaryWriter writer = null;

            //Create storage.
            returnValue = new MemoryStream();
            writer = new BinaryWriter(returnValue);

            //Copy data and return.
            CopyToStream(writer);
            writer = null;

            return returnValue;
        }
        /// <summary>
        /// Copies the contents of the collection to a stream.
        /// </summary>
        /// <returns>
        /// A new <see cref="VirtualMemoryStream"/> instance used to store the data content.
        /// </returns>
        public VirtualMemoryStream ToLargeStream()
        {
            VirtualMemoryStream returnValue = null;
            BinaryWriter writer = null;

            //Create storage.
            returnValue = new VirtualMemoryStream();
            writer = new BinaryWriter(returnValue);

            //Copy data and return.
            CopyToStream(writer);
            writer = null;

            return returnValue;
        }
        /// <summary>
        /// Copies the contents of the collection to the specified stream.
        /// </summary>
        /// <param name="writer">
        /// A <see cref="BinaryWriter"/> instance used to write the data to 
        /// an open <see cref="Stream"/> instance.
        /// </param>
        public void CopyToStream(BinaryWriter writer)
        {
            MoveFirst();
            while (!Eof)
            {
                Value.ToStream(writer);
                MoveNext();
            }
            writer.Flush();
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Reads the contents of the specified stream into the collection instance.
        /// </summary>
        /// <param name="stream">
        /// The <see cref="Stream"/> instance whose contents are being converted to
        /// data blocks.
        /// </param>
        private void ReadDataBlocks(Stream stream)
        {
            BinaryReader reader = null;

            if ((stream != null) && (stream.Length > 0))
            {
                reader = new BinaryReader(stream);
                ReadDataBlocks(reader);
                reader = null;
            }
        }
        /// <summary>
        /// Reads the contents of the specified stream into the collection instance.
        /// </summary>
        /// <param name="reader">
        /// A <see cref="BinaryReader"/> instance used to read data from an
        /// open stream.
        /// </param>
        private void ReadDataBlocks(BinaryReader reader)
        {
            DataBlock newItem = null;
            bool done = false;

            if ((reader != null) && (reader.BaseStream.Length > 0))
            {
                reader.BaseStream.Seek(0, SeekOrigin.Begin);
                do
                {
                    newItem = new DataBlock(_blockSize);
                    done = newItem.FromStream(reader);
                    Add(newItem);

                } while (!done);
            }
        }
        #endregion

    }
}
